feat(run): Propagates model_name from the gateway request through the runtime and persistence stack to the SQLite database.#2775
Conversation
…stence layer Pass model_name through the full run creation pipeline — from RunCreateRequest.context in the gateway, through RunManager, to the RunStore interface and SQL persistence. This enables client-specified model selection to be recorded per-run in the database.
|
@yitang
And there is also an edge case around effective model resolution. This PR currently records the requested model name, not the model that was actually used. If the requested model is invalid and fallback to the default model, the persisted |
|
@ggnnggez, thanks for looking at it and providing feedback. I think I understand the 1st and 3rd point, I can try to incorporate them into the next PR. Would you be able to elaborate a bit more on the 2nd point? I can see allowlist about skills, not about models. Thanks |
There was a problem hiding this comment.
Pull request overview
This PR threads a model_name value from the gateway run request (body.context) through the run lifecycle so it can be persisted on run records (in-memory store and SQL-backed repository), enabling auditing/cost/debug workflows based on the model used.
Changes:
- Extract
model_namefrom the gateway request context and pass it intoRunManager.create_or_reject(). - Extend the
RunRecord/RunStore.put()API surface to carrymodel_name. - Persist
model_namein both the in-memory RunStore and the SQL-backedrunstable viaRunRepository.put().
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| backend/app/gateway/services.py | Extracts model_name from request context and forwards it into run creation. |
| backend/packages/harness/deerflow/runtime/runs/manager.py | Adds model_name to RunRecord and persists it via the RunStore. |
| backend/packages/harness/deerflow/runtime/runs/store/base.py | Extends the RunStore put() interface to include model_name. |
| backend/packages/harness/deerflow/runtime/runs/store/memory.py | Stores model_name in the in-memory run record dict. |
| backend/packages/harness/deerflow/persistence/run/sql.py | Writes model_name into RunRow when persisting runs to SQL. |
| body_context = getattr(body, "context", None) or {} | ||
| model_name = body_context.get("model_name") | ||
|
|
| row = RunRow( | ||
| run_id=run_id, | ||
| thread_id=thread_id, | ||
| assistant_id=assistant_id, | ||
| user_id=resolved_user_id, | ||
| model_name=model_name, | ||
| status=status, |
| *, | ||
| on_disconnect: DisconnectMode = DisconnectMode.cancel, | ||
| metadata: dict | None = None, | ||
| kwargs: dict | None = None, | ||
| multitask_strategy: str = "reject", | ||
| model_name: str | None = None, | ||
| ) -> RunRecord: |
…pture - Validate model_name against allowlist in gateway services.py using get_app_config().get_model_config() - Truncate model_name to 128 chars to match DB column constraint - In worker.py, capture effective model name from agent.metadata after agent creation and persist if resolved differently than requested
…ip persistence tests - Add _normalize_model_name() to RunRepository for whitespace stripping and 128-char truncation before DB writes. - Add round-trip unit tests for model_name creation and default None in test_run_manager.py.
…n _normalize_model_name
|
@ggnnggez @WillemJiang updated the PR to address your comments and Copilot's comments. please have a look when you have time. |
model_name from the gateway request through the runtime and persistence stack to the SQLite database.model_name from the gateway request through the runtime and persistence stack to the SQLite database.
|
@yitang here are some additional comments
The SQLAlchemy model already declares model_name (model.py:23), but there is no migration file — the migrations/versions/ directory only has a .gitkeep. If this column doesn't exist in production databases, the
This calls a private method from outside the class, bypassing any coordination logic in RunManager. Consider exposing a public method like update_model_name(run_id, model_name) on RunManager that handles persistence and any necessary locking.
Tests only cover the MemoryRunStore path. The RunRepository path has normalization logic (_normalize_model_name) and the actual DB write — this is the critical production path and should have at least a basic integration test. |
…ay services Add isinstance check and str() coercion before calling .strip() to prevent AttributeError when non-string types (int, None, etc.) flow through the gateway. Paired with SQL integration test for end-to-end model_name persistence across gateway → langgraph → persistence layer.
…blic update method on RunManager - Drop a1b2c3d4e5f6 migration: model_name already exists in RunRow schema and is auto-created via Base.metadata.create_all() at startup - Add update_model_name() public method to RunManager to replace the private _persist_to_store call in worker.py, preserving internal locking/persistence
f825635 to
4d786eb
Compare
|
@WillemJiang thank you for your feedback. Please see the updated PR and below the description. I think it addresses all your comments. Migration for the model_name columnAs far as I can see, the I see it in the code, and I see it in my local database. So I think it is not applicable. Please let me know if otherwise. # backend/packages/harness/deerflow/persistence/engine.py:28-29:
def create_all(engine: Engine) -> None:
Base.metadata.create_all(engine)
# Base is imported from backend/packages/harness/deerflow/persistence/models/__init__.py:29:
Base = declarative_base()
register_models(Base) # <-- registers RunRowcalling private function externallyworker.py now calls the new public function update_model_name additional test on the SQL persistenceThe new test uses the production SQL path (RunRepository) , and it covers 4 cases:
|
Changes:
services.py: Extract model_name from RunCreateRequest.contextmanager.py: RunRecord dataclass and create_or_reject() accept model_namebase.py: RunStore.put() interface includes model_name parametermemory.py: MemoryRunStore implementation updatedsql.py: RunRepository.put() writes model_name to databaseVerified: model_name correctly persisted in deerflow.db (tested manually with gpt-4o)
Enables users to track which model was used for each run by persisting
model_nameto the database. This supports auditability, cost tracking, and debugging by making model usage visible in stored run records.